#!/usr/bin/env python3
"""GitLogAnalyzer - 分析 Git 仓库的提交记录并生成统计信息和可视化图表"""

import os
import re
import sys
import argparse
import subprocess
from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from collections import Counter

# 设置中文字体支持
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题

class GitLogAnalyzer:
    def __init__(self, repo_path):
        """初始化分析器并验证仓库路径"""
        self.repo_path = repo_path
        self.commits = []
        self.df = None
        
        # 验证仓库路径是否存在且为有效 Git 仓库
        if not os.path.exists(repo_path):
            raise FileNotFoundError(f"错误: 路径 {repo_path} 不存在")
            
        try:
            # 检查是否为 Git 仓库
            subprocess.run(
                ["git", "-C", repo_path, "rev-parse", "--is-inside-work-tree"],
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
        except subprocess.CalledProcessError:
            raise NotADirectoryError(f"错误: {repo_path} 不是一个有效的 Git 仓库")
    
    def analyze(self):
        """执行 Git 日志分析"""
        print(f"正在分析仓库: {self.repo_path}")
        
        # 获取 Git 日志
        self._fetch_git_log()
        
        # 转换为 DataFrame 进行分析
        self._convert_to_dataframe()
        
        # 生成统计信息
        self._generate_statistics()
        
        # 生成可视化图表
        self._generate_visualizations()
        
        print("分析完成! 统计信息和图表已生成。")
    
    def _fetch_git_log(self):
        """获取 Git 日志并解析提交信息"""
        try:
            # 执行 git log 命令获取详细提交信息
            result = subprocess.run(
                [
                    "git", "-C", self.repo_path, "log", 
                    "--pretty=format:%H|%at|%an|%ae|%s",
                    "--numstat",
                    "--no-renames"
                ],
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
            
            # 解析提交日志
            log_lines = result.stdout.split('\n')
            current_commit = None
            
            for line in log_lines:
                if not line.strip():
                    continue
                    
                # 新提交的开始
                if re.match(r'^[0-9a-f]{40}\|', line):
                    if current_commit:
                        self.commits.append(current_commit)
                    
                    parts = line.split('|', 4)
                    current_commit = {
                        'hash': parts[0],
                        'timestamp': int(parts[1]),
                        'author': parts[2],
                        'email': parts[3],
                        'message': parts[4],
                        'date': datetime.fromtimestamp(int(parts[1])),
                        'insertions': 0,
                        'deletions': 0,
                        'files_changed': 0,
                        'extensions': Counter()
                    }
                # 提交的文件变更统计
                elif current_commit and line[0].isdigit():
                    parts = line.split('\t', 2)
                    if len(parts) == 3:
                        insertions, deletions, filename = parts
                        current_commit['insertions'] += int(insertions) if insertions != '-' else 0
                        current_commit['deletions'] += int(deletions) if deletions != '-' else 0
                        current_commit['files_changed'] += 1
                        
                        # 统计文件扩展名
                        ext = os.path.splitext(filename)[1].lower()
                        current_commit['extensions'][ext] += 1
            
            # 添加最后一个提交
            if current_commit:
                self.commits.append(current_commit)
                
            print(f"已解析 {len(self.commits)} 条提交记录")
            
        except subprocess.CalledProcessError as e:
            print(f"错误: 获取 Git 日志失败: {e.stderr}")
            sys.exit(1)
    
    def _convert_to_dataframe(self):
        """将提交数据转换为 Pandas DataFrame"""
        if not self.commits:
            print("错误: 没有提交数据可分析")
            sys.exit(1)
            
        # 创建 DataFrame
        self.df = pd.DataFrame(self.commits)
        
        # 添加更多特征
        self.df['year'] = self.df['date'].dt.year
        self.df['month'] = self.df['date'].dt.month
        self.df['day'] = self.df['date'].dt.day
        self.df['hour'] = self.df['date'].dt.hour
        self.df['day_of_week'] = self.df['date'].dt.day_name()
        self.df['lines_changed'] = self.df['insertions'] + self.df['deletions']
        
        # 提取主要编程语言/文件类型
        self.df['primary_ext'] = self.df['extensions'].apply(
            lambda x: max(x, key=x.get) if x else ''
        )
    
    def _generate_statistics(self):
        """生成并打印统计信息"""
        if self.df is None or self.df.empty:
            return
            
        print("\n===== 仓库统计信息 =====")
        
        # 基本统计
        print(f"总提交数: {len(self.df)}")
        print(f"首次提交: {self.df['date'].min()}")
        print(f"最近提交: {self.df['date'].max()}")
        print(f"活跃天数: {(self.df['date'].max() - self.df['date'].min()).days}")
        
        # 开发者统计
        developers = self.df['author'].nunique()
        print(f"\n开发者数量: {developers}")
        
        top_authors = self.df['author'].value_counts().head(5)
        print("\n提交最多的开发者:")
        for author, count in top_authors.items():
            print(f"  - {author}: {count} 次提交")
        
        # 代码变更统计
        total_insertions = self.df['insertions'].sum()
        total_deletions = self.df['deletions'].sum()
        print(f"\n总插入行数: {total_insertions:,}")
        print(f"总删除行数: {total_deletions:,}")
        print(f"净变更行数: {total_insertions - total_deletions:,}")
        
        # 提交频率统计
        daily_commits = self.df.groupby(pd.Grouper(key='date', freq='D')).size()
        print(f"\n平均每日提交数: {daily_commits.mean():.2f}")
        print(f"最高单日提交数: {daily_commits.max()}")
        
        # 文件类型统计
        file_types = self.df['primary_ext'].value_counts().head(5)
        print("\n最常修改的文件类型:")
        for ext, count in file_types.items():
            print(f"  - {ext}: {count} 次修改")
    
    def _generate_visualizations(self):
        """生成可视化图表"""
        if self.df is None or self.df.empty:
            return
            
        print("\n生成可视化图表...")
        
        output_dir = os.path.join(os.getcwd(), "git_analysis_results")
        os.makedirs(output_dir, exist_ok=True)
        
        # 1. 提交时间分布
        plt.figure(figsize=(12, 6))
        sns.countplot(x='hour', data=self.df)
        plt.title('按小时统计的提交分布')
        plt.xlabel('小时')
        plt.ylabel('提交数量')
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, 'commit_hourly.png'))
        
        # 2. 每周提交分布
        plt.figure(figsize=(12, 6))
        day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
        sns.countplot(x='day_of_week', data=self.df, order=day_order)
        plt.title('按星期统计的提交分布')
        plt.xlabel('星期')
        plt.ylabel('提交数量')
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, 'commit_weekly.png'))
        
        # 3. 开发者贡献分布
        plt.figure(figsize=(12, 8))
        author_counts = self.df['author'].value_counts()
        if len(author_counts) > 10:
            author_counts = author_counts.head(10)
        sns.barplot(x=author_counts.values, y=author_counts.index)
        plt.title('开发者提交贡献分布')
        plt.xlabel('提交数量')
        plt.ylabel('开发者')
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, 'developer_contribution.png'))
        
        # 4. 随时间的提交趋势
        plt.figure(figsize=(12, 6))
        self.df['date'].dt.to_period('M').value_counts().sort_index().plot(kind='line')
        plt.title('每月提交趋势')
        plt.xlabel('日期')
        plt.ylabel('提交数量')
        plt.xticks(rotation=45)
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, 'commit_trend.png'))
        
        # 5. 代码变更热力图
        pivot_table = self.df.pivot_table(
            index='day_of_week',
            columns='hour',
            values='lines_changed',
            aggfunc='sum',
            fill_value=0
        )
        pivot_table = pivot_table.reindex(day_order)
        
        plt.figure(figsize=(14, 8))
        sns.heatmap(pivot_table, cmap='YlGnBu', annot=True, fmt='g')
        plt.title('按时间统计的代码变更热力图')
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, 'code_change_heatmap.png'))
        
        print(f"图表已保存到: {output_dir}")

def main():
    """主函数，处理命令行参数并启动分析"""
    parser = argparse.ArgumentParser(description='Git 提交记录分析工具')
    parser.add_argument('repo_path', help='Git 仓库路径')
    
    args = parser.parse_args()
    
    try:
        analyzer = GitLogAnalyzer(args.repo_path)
        analyzer.analyze()
    except Exception as e:
        print(f"错误: {str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()